package net.pimpas.configuration;

import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.pimpas.core.configuration.Configuration;
import net.pimpas.core.configuration.exception.ConfigurationException;
import net.pimpas.core.configuration.file.FileConfiguration;
import net.pimpas.framework.Framework;
import net.pimpas.scanner.ClassPathScanner;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

public class PimpasFrameworkConfiguration implements Configuration<FileConfiguration> {
	private List<FileConfiguration> filesConfiguration;
	private Logger logger = Logger.getLogger(PimpasFrameworkConfiguration.class);
	private Framework pimpas;
	private boolean skipClasspathScanner;
	private int cleanup;
	
	public PimpasFrameworkConfiguration() {
		this.setFilesConfiguration(new ArrayList<FileConfiguration>());
		this.setCleanup(-1);
	}

	@SuppressWarnings("unchecked")
	public void consume() throws ConfigurationException {
        
        // TODO need xml to configure anything?
		// ANSWER: No, its impossible config ALL FUCK configuration with xml
		// like in hibernate, we need scan all entitys classess, how to do without working scanner?
		// ANSWER2: Yes, in web start, we can use simple serviceproviders like logging, xml, dont need
		// any visitor.
		if(this.getFilesConfiguration() == null || this.getFilesConfiguration().size() == 0)
			return;
		FileConfiguration fc = null;
		URL url = null;
		Document doc = null;
		Node t = null;
		for(int i=0;i<this.getFilesConfiguration().size();i++) {
			fc = this.getFilesConfiguration().get(i);			
			url = fc.getFileUrl();
			logger.debug("[Parsing Configuration File: "+url.getFile()+"]");
			try {
				doc = new SAXReader().read(url);
				this.addServiceProviders(doc);
				this.addIgnoreJars(doc);
				t = doc.selectSingleNode("//pimpas//config//scanner//alive");
				if(t != null)
					if(!this.isSkipClasspathScanner()) this.setSkipClasspathScanner(!Boolean.parseBoolean(t.getText()));
				t = doc.selectSingleNode("//pimpas//config//cleanup");
				if(t != null)
					this.setCleanup(Integer.parseInt(t.getText()));
			} catch (DocumentException e) {
				throw new ConfigurationException(e);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	private void addIgnoreJars(Document doc) throws ConfigurationException {
		List<Node> t = null;
		Node n = null;
		String name = null;
		t = doc.selectNodes("//pimpas//config//ignorejars//ignorejar");
		for(Iterator<Node>it = t.iterator();it.hasNext();) {
			n = it.next();
			name = n.selectSingleNode("name").getText();
			ClassPathScanner.addIgnoreJar(name);
		}
	}
	
	@SuppressWarnings("unchecked")
	private void addServiceProviders(Document doc) throws ConfigurationException {
		List<Node> t = null;
		String type = null;
		String provider = null;
		Class clazzT = null;
		Class clazzP = null;
		Node n = null;
		t = doc.selectNodes("//pimpas//config//service-providers//service-provider");
		n = null;
		for(Iterator<Node> it = t.iterator();it.hasNext();) {
			n = it.next();
			type = n.selectSingleNode("type").getText();
			provider = n.selectSingleNode("provider").getText();
			try {
				clazzT = Class.forName(type);
				clazzP = Class.forName(provider);
				this.getPimpas().registerServiceProvider(clazzT, clazzP);
			} catch (ClassNotFoundException e) {
				throw new ConfigurationException(e);
			}
		}		
	}

	public List<FileConfiguration> getFilesConfiguration() {
		return this.filesConfiguration;
	}

	public String getName() {
		return this.getClass().getSimpleName();
	}

	private void setFilesConfiguration(List<FileConfiguration> filesConfiguration) {
		this.filesConfiguration = filesConfiguration;
	}

	public Framework getPimpas() {
		return pimpas;
	}

	public void setPimpas(Framework pimpas) {
		this.pimpas = pimpas;
	}

	public List<FileConfiguration> getConfigurationSources() {
		return this.getFilesConfiguration();
	}

	public boolean isSkipClasspathScanner() {
		return skipClasspathScanner;
	}

	public void setSkipClasspathScanner(boolean skipClasspathScanner) {
		this.skipClasspathScanner = skipClasspathScanner;
	}

	public int getCleanup() {
		return cleanup;
	}

	public void setCleanup(int cleanup) {
		this.cleanup = cleanup;
	}
}